/*
 * SIT_EditBox.c : simple single/multi-line edit box (not rich edit).
 *
 * Written by T.Pierron, Oct 11, 2006.
 */

#include "SIT_P.h"
#include <stdio.h>

	TagList EditBoxClass[] = {
		{ SIT_SuperClass,  NULL,          ___, SIT_INT,  (ULONG) WidgetClass },
		{ SIT_AlignHoriz,  "alignHoriz",  __G, SIT_INT,  OFFSET(SIT_EditBox, se_Flags) },
		{ SIT_EditType,    "editType",    __G, SIT_INT,  OFFSET(SIT_EditBox, se_EditType) },
		{ SIT_TempEdit,    "tempEdit",    _SG, SIT_BOOL, OFFSET(SIT_EditBox, se_TempEdit) },
		{ SIT_ReadOnly,    "readOnly",    _SG, SIT_BOOL, OFFSET(SIT_EditBox, se_ReadOnly) },
		{ SIT_StartSel,    "startSel",    _SG, SIT_INT,  OFFSET(SIT_EditBox, se_StartSel) },
		{ SIT_EndSel,      "endSel",      _SG, SIT_INT,  OFFSET(SIT_EditBox, se_EndSel) },
		{ SIT_MinValue,    "minValue",    _SG, SIT_INT,  OFFSET(SIT_EditBox, se_MinValue) },
		{ SIT_MaxValue,    "maxValue",    _SG, SIT_INT,  OFFSET(SIT_EditBox, se_MaxValue) },
		{ SIT_ScrollPos,   "scrollPos",   _SG, SIT_PTR,  OFFSET(SIT_EditBox, se_CurValue) },
		{ SIT_PlaceHolder, "placeHolder", _SG, SIT_STR,  OFFSET(SIT_EditBox, se_CueBanner) },
		{ SIT_HotKey,      "hotKey",      _SG, SIT_INT,  OFFSET(SIT_EditBox, se_HotKey) },
		{ SIT_BuddyEdit,   "buddyEdit",   _SG, SIT_PTR,  OFFSET(SIT_EditBox, se_BuddyTB) },
		{ SIT_TagEnd }
	};

#define	edit    ((SIT_EditBox)w)
static int SIT_MeasureEditBox(SIT_Widget w, APTR cd, APTR unused2)
{
	HDC     hdc = GetDC(w->sw_Handle);
	HGDIOBJ old = SelectObject(hdc, w->sw_Font);
	SIZE *  ret = cd;
	LPWSTR  str;

	if (IsDef(w->sw_Title))
		UTF8ToUTF16(w->sw_Title, str)
	else
		str = L"X";

	GetTextExtentPoint32(hdc, str, wcslen(str), ret);

	ret->cx += 10;
	if (edit->se_EditType != SITV_BorderlessEdit)
		ret->cy += 6;

	SelectObject(hdc, old);
	ReleaseDC(w->sw_Handle, hdc);
	return 0;
}

static int SIT_VKeyToCtrl(int vkey)
{
	int flags = 0, key = vkey & 0xff;
	if (vkey & SITK_FlagShift) flags |= HOTKEYF_SHIFT;
	if (vkey & SITK_FlagCtrl)  flags |= HOTKEYF_CONTROL;
	if (vkey & SITK_FlagAlt)   flags |= HOTKEYF_ALT;
	if (vkey & SITK_FlagCmd)   flags |= HOTKEYF_EXT;
	return MAKEWORD(key <= 127 ? key : sit2vk[vkey >> 16], flags);
}

static int ByteToChar(STRPTR utf8, int bytes)
{
	STRPTR p, e;
	int    n;

	for (p = utf8, e = p - bytes, n = 0; *p && p < e; n ++, p ++) {
		unsigned char cp = *p;
		int nb = 1;
		if ((cp & 0xe0) == 0xc0) nb = 2; else
		if ((cp & 0xf0) == 0xe0) nb = 3; else
		if ((cp & 0xf8) == 0xf0) nb = 4;
		while (*p && nb) p ++, nb --;
	}
	return n;
}

static int SIT_PostProcessEdit(SIT_Widget w, APTR call_data, APTR user_data)
{
	ULONG ws = GetWindowLong(w->sw_Handle, GWL_STYLE);
	ULONG old = ws;

	if (edit->se_Flags & 4) /* Alignment style changed */
	{
		ws &= ~(ES_LEFT|ES_CENTER|ES_RIGHT);
		ws |= edit->se_Flags & 3;
	}
	if (edit->se_Flags & 8) /* Integer range/pos */
	{
		SendMessage(edit->se_UpDown, UDM_SETRANGE32, edit->se_MinValue, edit->se_MaxValue);
		if (edit->se_CurValue)
			SendMessage(edit->se_UpDown, UDM_SETPOS32, 0, *edit->se_CurValue);
	}
	if (edit->se_Flags & 16) /* Read only */
	{
		if (edit->se_ReadOnly) ws |= ES_READONLY;
		else ws &= ~ES_READONLY;
	}
	if (edit->se_Flags & 96)
	{
		int flg = edit->se_Flags & 96;
		if (flg == 32) edit->se_EndSel = edit->se_StartSel;
		if (flg == 64) edit->se_StartSel = edit->se_EndSel;
		// XXX sw_Title might not be synced
		if (edit->se_StartSel < 0) edit->se_StartSel = ByteToChar(w->sw_Title, edit->se_StartSel);
		if (edit->se_EndSel   < 0) edit->se_EndSel   = ByteToChar(w->sw_Title, edit->se_EndSel);
		Edit_SetSel(w->sw_Handle, edit->se_StartSel, edit->se_EndSel);
	}
	if (edit->se_Flags & 128)
	{
		#define	EM_SETCUEBANNER    0x1501
		LPWSTR cueban;
		UTF8ToUTF16(edit->se_CueBanner, cueban);
		SendMessage(w->sw_Handle, EM_SETCUEBANNER, 0, (LPARAM) cueban);
	}
	if (edit->se_Flags & 256)
	{
		SIT_Dialog d;
		for (d = (APTR) w->sw_Parent; d->super.sw_Type != SIT_DIALOG; d = (APTR) d->super.sw_Parent);
		if (! edit->se_TempEdit) {
			if (d->sd_Capture == w->sw_Handle)
				d->sd_Capture = 0;
		}
		else d->sd_Capture = w->sw_Handle;
	}
	if (old != ws) SetWindowLong(w->sw_Handle, GWL_STYLE, ws);
	edit->se_Flags &= 0x3;
	w->sw_PostProcess = NULL;
	return 0;
}

static int SIT_SetEditValues(SIT_Widget w, APTR cd, APTR user_data)
{
	SIT_Variant * value = user_data;

	switch (((TagList *)cd)->tl_TagID) {
	case SIT_AlignHoriz: edit->se_Flags &= ~0x7; edit->se_Flags |= value->integer | 4; break;
	case SIT_MinValue:   edit->se_MinValue = value->integer; edit->se_Flags |= 8; break;
	case SIT_MaxValue:   edit->se_MaxValue = value->integer; edit->se_Flags |= 8; break;
	case SIT_ScrollPos:  edit->se_CurValue = value->pointer; edit->se_Flags |= 8; break;
	case SIT_ReadOnly:   edit->se_ReadOnly = value->boolean; edit->se_Flags |= 16; break;
	case SIT_StartSel:   edit->se_StartSel = value->integer; edit->se_Flags |= 32; break;
	case SIT_EndSel:     edit->se_EndSel   = value->integer; edit->se_Flags |= 64; break;
	case SIT_TempEdit:   edit->se_TempEdit = value->boolean; edit->se_Flags |= 256; break;
	case SIT_HotKey:
		SendMessage(w->sw_Handle, HKM_SETHOTKEY, SIT_VKeyToCtrl(edit->se_HotKey = value->integer), 0);
		break;
	case SIT_BuddyEdit:
		w = value->pointer;
		if (w->sw_Type == SIT_SLIDER) /* It will trigger event back to edit */
		{
			((SIT_Slider)w)->tb_Buddy = &edit->super;
			edit->se_BuddyTB = w;
			return 1;
		}
		return 0;
	case SIT_PlaceHolder:
		edit->se_Flags |= 128;
		// no break;
	default: SIT_SetWidgetValue(w, cd, user_data);
	}
	if (edit->se_Flags & ~0x3)
		w->sw_PostProcess = SIT_PostProcessEdit;
	return 0;
}
#undef edit

/* Wants keypress */
static LRESULT CALLBACK SIT_EditSubClassHandler(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	SIT_Widget w = (SIT_Widget) GetProp(wnd, sit_class);

	if (msg == WM_GETDLGCODE)
	{
		LRESULT res = SIT_SubClassHandler(wnd, msg, wParam, lParam);
		if (res) return res;
	}
	return CallWindowProc(w->sw_SuperProc, wnd, msg, wParam, lParam);
}

BOOL SIT_CreateEditBox(SIT_Widget w, va_list args)
{
	SIT_EditBox edit  = (SIT_EditBox) w;
	int         style = WS_CHILD;
	int         ext   = WS_EX_CLIENTEDGE;
	HWND        hwnd;

	w->sw_OptimalWidth = SIT_MeasureEditBox;
	w->sw_SetValue     = SIT_SetEditValues;
	edit->se_MaxValue  = 100;

	SIT_ParseTags(w, args, w->sw_Class = EditBoxClass);

	switch (edit->se_EditType) {
	case SITV_BorderlessEdit: ext = 0; // no break;
	case 0:                   style |= ES_AUTOHSCROLL; break;
	case SITV_Password:       style |= ES_PASSWORD; break;
	case SITV_MultiLineEdit:  style |= ES_MULTILINE | ES_AUTOVSCROLL;
	}
	if (edit->se_ReadOnly) style |= ES_READONLY;
	if (IsVisible(w))      style |= WS_VISIBLE;
	if (HAS_TAB(w))        style |= WS_TABSTOP;

	/* Create Win32 control, pos and dim will be set later */
	hwnd = w->sw_Handle = CreateWindowEx(ext,
		edit->se_EditType == SITV_HotKey ? HOTKEY_CLASS : WC_EDIT,
		w->sw_Name, style, 0, 0, 0, 0, SIT_GetContainer(w),
		(HMENU) MAKE_ID(w), instance, NULL
	);

	if (hwnd == NULL) return FALSE;
	SIT_SetDefaultFont(w);

	switch (edit->se_EditType) {
	case SITV_HotKey:
		SendMessage(hwnd, HKM_SETHOTKEY, SIT_VKeyToCtrl(edit->se_HotKey), 0);
		break;

	case SITV_Integer:
		edit->se_UpDown = CreateWindow(UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE |
			UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_SETBUDDYINT, 0, 0,
			GetSystemMetrics(SM_CYHSCROLL), 20, SIT_GetContainer(w), (HMENU) MAKE_ID(w),
			instance, NULL);

		SendMessage(edit->se_UpDown, UDM_SETBUDDY, (WPARAM) hwnd, 0);
		if (edit->se_CurValue)
		{
			/* Track value into user-supplied variable */
			SetProp(edit->se_UpDown, sit_class, (HANDLE) w);
			SendMessage(edit->se_UpDown, UDM_SETRANGE32, edit->se_MinValue, edit->se_MaxValue);
			SendMessage(edit->se_UpDown, UDM_SETPOS32, 0, *edit->se_CurValue);
		}
		if (w->sw_Title == NULL) break;

	default:
		SIT_SetTitle(w);
	}

	if (w->sw_PostProcess) w->sw_PostProcess(w, NULL, NULL);

	/* Keep track of class inside callback */
	SetProp(hwnd, sit_class, (HANDLE) w);

	/* Subclass to trap KeyDown, KillFocus and SetFocus */
	w->sw_SuperProc = (APTR) GetWindowLong(w->sw_Handle, GWL_WNDPROC);
	SetWindowLong(w->sw_Handle, GWL_WNDPROC, (LONG) SIT_EditSubClassHandler);

	return TRUE;
}

Bool SIT_EditGetText(SIT_EditBox ed)
{
	#define	w       (&ed->super)
	if (ed->se_EditType != SITV_HotKey)
	{
		LPWSTR buf;
		int    len = GetWindowTextLength(w->sw_Handle);

		if (ed->se_BufLen == 0 && w->sw_Title)
			ed->se_BufLen = strlen(w->sw_Title);

		if (len > 0)
		{
			/* Get content in UTF-16 */
			buf = alloca(len * sizeof *buf);

			GetWindowText(ed->super.sw_Handle, buf, len+1);

			/* Compute length used by this string in UTF-8 */
			int utf8len = WideCharToMultiByte(CP_UTF8, 0, buf, len, NULL, 0, NULL, NULL);

			/* Alloc a buffer large enough */
			if (ed->se_BufLen < utf8len)
			{
				int    newmax = (utf8len + 64) & ~63; // +1 for NUL
				STRPTR newbuf = realloc(w->sw_Title, newmax);

				if (newbuf)
				{
					ed->se_BufLen = newmax;
					w->sw_Title = newbuf;
				}
				else return False;
			}

			/* Do the conversion */
			WideCharToMultiByte(CP_UTF8, 0, buf, len, w->sw_Title, ed->se_BufLen, NULL, NULL);
			w->sw_Title[len] = 0;
		}
		else if (w->sw_Title)
		{
			/* Empty string */
			w->sw_Title[0] = 0;
		}
	}
	else /* Convert VK to SITK_* */
	{
		int code = LOWORD(SendMessage(w->sw_Handle, HKM_GETHOTKEY, 0, 0));
		int qual = HIBYTE(code);

		ed->se_HotKey = 0;
		if (qual & HOTKEYF_ALT)     ed->se_HotKey |= SITK_FlagAlt;
		if (qual & HOTKEYF_CONTROL) ed->se_HotKey |= SITK_FlagCtrl;
		if (qual & HOTKEYF_EXT)     ed->se_HotKey |= SITK_FlagCmd;
		if (qual & HOTKEYF_SHIFT)   ed->se_HotKey |= SITK_FlagShift;

		DATA8 ptr = memchr(sit2vk, code = LOBYTE(code), SITK_Help - SITK_Home + 1);

		ed->se_HotKey |= (ptr ? SITK_Home + (ptr - sit2vk) : code);
	}
	return True;
	#undef w
}

